package net.gdface.utils;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.base.Throwables;
import com.google.common.primitives.Primitives;

import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.STATIC;

/**
 * 深度对象克隆工具
 * @author guyadong
 * @since 2.8.1
 */
public class DeepCloneUtils {

	/**
	 * (深度克隆)复制数据对象的所有字段,输入参数为{@code null}返回{@code null}<br>
	 * 基本数据类型(primitive,String)不做复制,其他类型字段执行{@link #deepClone(Object) }方法深度克隆
	 * @param input
	 * @param ifVolatile 定义对 volatile 字段的处理方式:
	 * <ul>
	 * <li>1    -- 字段设置为{@code null} </li>
	 * <li>2    -- 执行克隆 </li>
	 * <li>其他    -- 跳过此字段 </li>
	 * </ul>
	 * @return always input
	 * @see #deepClone(Object)
	 */
	public static <T>T cloneFields(T input, int ifVolatile){
		if(noClone(input)){
			/** 基本类型不需要复制 */
			return input;
		}
		for(Class<?> clazz=input.getClass();null != clazz && !Object.class.equals(clazz);clazz = clazz.getSuperclass()) {
			for(Field field:clazz.getDeclaredFields()) {
				try {
					if((field.getModifiers() & (STATIC+FINAL)) == 0 && !noClone(field.getType())){
						field.setAccessible(true);
						if(!Modifier.isVolatile(field.getModifiers())) {
							field.set(input, deepClone(field.get(input)));
						}else if(1 == ifVolatile) {
							field.set(input, null);
						}else if(2 == ifVolatile) {
							field.set(input, deepClone(field.get(input)));
						}
					}
				} catch (IllegalArgumentException | IllegalAccessException e) {
					Throwables.throwIfUnchecked(e);
					throw new RuntimeException(e);
				}
			}
		}
		return input;
	}
	/**
	 * (深度克隆)对数据对象的所有字段执行深度复制,输入参数为{@code null}返回{@code null}<br>
	 * @see #cloneFields(Object, int)
	 */
	public static <T>T cloneFields(T input){
		return cloneFields(input,2);
	}
	/**
	 * [递归]复制数据对象(深度克隆),输入参数为{@code null}返回{@code null}<br>
	 * 基本数据类型(primitive,String)不做复制,原样返回,
	 * 实现 {@link Cloneable} 接口的对象执行 clone 方法,
	 * {@link Map}和{@link Collection}接口实现如果有默认构造方法执行{@code putAll,addAll}方法完成对象复制,
	 * 如果对象类型有复制构造方法，调用复制构造方法完成对象复制,
	 * 实现 {@link Serializable}接口的对象基于对象的序列化反序列化实现对象复制
	 * @param input
	 */
	@SuppressWarnings("unchecked")
	public static <T>T deepClone(T input){
		if(noClone(input)){
			/** 基本类型不需要复制 */
			return input;
		}
		T cloned;
		if(input instanceof Collection){
			if(input != (cloned = tryConstructContainer(input,Collection.class))){
				return cloned;
			}
		}
		if (input instanceof Map) {
			if(input != (cloned = tryConstructContainer(input,Map.class))){
				return cloned;
			}
		}
		if(input instanceof Cloneable){
			if(input.getClass().isArray()){
				Class<?> componentType = input.getClass().getComponentType();
				int length =  Array.getLength(input);
				cloned = (T) Array.newInstance(componentType, length);
				System.arraycopy(input, 0, cloned, 0, length);
				if(!noClone(componentType)) {
					for(int i=0,end_i=Array.getLength(cloned);i<end_i;++i) {
						Array.set(cloned, i, deepClone(Array.get(cloned, i)));
					}
				}
			}else {
				cloned = callClone(input);
			}
			return (T) cloned;
		}

		if(input instanceof Serializable){
			return (T) SerializationUtils.clone((Serializable) input);
		}
		if(input instanceof ByteBuffer) {
			return (T) ((ByteBuffer)input).duplicate();
		}
		if(input instanceof CharBuffer) {
			return (T) ((CharBuffer)input).duplicate();
		}
		if(input instanceof IntBuffer) {
			return (T) ((IntBuffer)input).duplicate(); 
		}
		if(input instanceof ShortBuffer) {
			return (T) ((ShortBuffer)input).duplicate(); 
		}
		if(input instanceof LongBuffer) {
			return (T) ((LongBuffer)input).duplicate(); 
		}
		if(input instanceof FloatBuffer) {
			return (T) ((FloatBuffer)input).duplicate(); 
		}
		if(input instanceof DoubleBuffer) {
			return (T) ((DoubleBuffer)input).duplicate(); 
		}
		throw new IllegalArgumentException("NO SUPPORT CLONE "+input.getClass());
	}

	@SuppressWarnings({ "unchecked" })
	private static <T>T callClone(T input) {
		try {
			return (T) input.getClass().getMethod("clone").invoke(input);
		} catch (Exception e) {
			Throwables.throwIfUnchecked(e);
			throw new RuntimeException(e);
		}
	}
	private static boolean noClone(Object object){
		if(null == object){
			return true;
		}
		return noClone(object.getClass());
	}
	private static boolean noClone(Class<?> clazz){
		if(Primitives.unwrap(clazz).isPrimitive()){
			return true;
		}
		if(String.class.equals(clazz)){
			return true;
		}
		return false;
	}

	/**
	 * {@link Map},{@link Collection}类型,如果有默认构造方法使用{@code putAll,addAll}方法完成对象复制
	 * @param input
	 * @param targetType
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static <T>T tryConstructContainer(T input,Class<?>targetType){
		if(targetType.isInstance(input)){
			try{
				Object output ;
				if(input instanceof Map || input instanceof Collection){
					output = input.getClass().getConstructor().newInstance();
					if(output instanceof Map){
						Set<Entry> entrySet = ((Map)input).entrySet();
						for(Entry entry:entrySet) {
							((Map)output).put(entry.getKey(), deepClone(entry.getValue()));
						}
						return (T) output;
					}else if(output instanceof Collection){
						for(Object element:(Collection)input) {
							((Collection)output).add(deepClone(element));
						}
						return (T) output;
					}
				}
			} catch (Exception e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}
		return input;
	}

}